package com.github.sundeepk.compactcalendarview;


import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.ComponentContainer;

class AnimationHandler {
    private static final int HEIGHT_ANIM_DURATION_MILLIS = 650;
    private static final int INDICATOR_ANIM_DURATION_MILLIS = 200;
    private boolean isAnimating = false;
    private final CompactCalendarController compactCalendarController;
    private final CompactCalendarView compactCalendarView;
    private CompactCalendarView.CompactCalendarAnimationListener compactCalendarAnimationListener;

    AnimationHandler(
            CompactCalendarController compactCalendarController,
            CompactCalendarView compactCalendarView) {
        this.compactCalendarController = compactCalendarController;
        this.compactCalendarView = compactCalendarView;
    }

    void setCompactCalendarAnimationListener(
            CompactCalendarView.CompactCalendarAnimationListener
                    compactCalendarAnimationListener) {
        this.compactCalendarAnimationListener = compactCalendarAnimationListener;
    }

    void openCalendar() {
        if (isAnimating) {
            return;
        }
        isAnimating = true;
        ComponentContainer.LayoutConfig layoutConfig = compactCalendarView.getLayoutConfig();
        layoutConfig.height = 0;
        compactCalendarView.setLayoutConfig(layoutConfig);
        compactCalendarView.postLayout();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setDuration(HEIGHT_ANIM_DURATION_MILLIS);

        animatorValue.setValueUpdateListener((animatorValue1, progress) -> {
            layoutConfig.height = (int) (compactCalendarController.getTargetHeight() * progress);
            float grow = (progress * (AnimationHandler.this.getTargetGrowRadius() * 2));
            compactCalendarController.setGrowProgress(grow);
            compactCalendarView.setLayoutConfig(layoutConfig);
            compactCalendarView.invalidate();
        });
        setUpAnimationLisForOpen(animatorValue);
        animatorValue.start();
    }

    void closeCalendar() {
        if (isAnimating) {
            return;
        }
        isAnimating = true;
        ComponentContainer.LayoutConfig layoutConfig = compactCalendarView.getLayoutConfig();
        layoutConfig.height = compactCalendarView.getHeight();
        compactCalendarView.setLayoutConfig(layoutConfig);
        compactCalendarView.postLayout();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setDuration(HEIGHT_ANIM_DURATION_MILLIS);
        animatorValue.setValueUpdateListener((animatorValue1, progress) -> {
            layoutConfig.height = (int) (compactCalendarController.getTargetHeight() -
                    compactCalendarController.getTargetHeight() * progress);
            compactCalendarView.setLayoutConfig(layoutConfig);
            float grow = ((1 - progress) * (getTargetGrowRadius() * 2));
            compactCalendarController.setGrowProgress(grow);
            compactCalendarView.invalidate();
        });
        setUpAnimationLisForClose(animatorValue);
        animatorValue.start();
    }

    void openCalendarWithAnimation() {
        if (isAnimating) {
            return;
        }
        isAnimating = true;
        ComponentContainer.LayoutConfig layoutConfig = compactCalendarView.getLayoutConfig();
        layoutConfig.height = 0;
        compactCalendarView.setLayoutConfig(layoutConfig);
        compactCalendarView.postLayout();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setDuration(HEIGHT_ANIM_DURATION_MILLIS);
        animatorValue.setValueUpdateListener((animatorValue1, progress) -> {
            layoutConfig.height = (int) (compactCalendarController.getTargetHeight() * progress);
            float grow = (progress * (getTargetGrowRadius() * 2));
            compactCalendarController.setGrowProgress(grow);
            compactCalendarView.setLayoutConfig(layoutConfig);
            compactCalendarView.invalidate();
        });
        AnimatorValue indicatorAnim = getIndicatorAnimator(true);
        setUpAnimationLisForExposeOpen(indicatorAnim, animatorValue);
        animatorValue.start();
    }

    void closeCalendarWithAnimation() {
        if (isAnimating) {
            return;
        }
        isAnimating = true;
        ComponentContainer.LayoutConfig layoutConfig = compactCalendarView.getLayoutConfig();
        layoutConfig.height = compactCalendarView.getHeight();
        compactCalendarView.setLayoutConfig(layoutConfig);
        compactCalendarView.postLayout();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener((animatorValue1, progress) -> {
            layoutConfig.height = (int) (compactCalendarController.getTargetHeight() -
                    compactCalendarController.getTargetHeight() * progress);
            float grow = ((1 - progress) * (getTargetGrowRadius() * 2));
            compactCalendarController.setGrowProgress(grow);
            compactCalendarView.setLayoutConfig(layoutConfig);
            compactCalendarView.invalidate();
        });
        animatorValue.setDuration(HEIGHT_ANIM_DURATION_MILLIS);
        AnimatorValue indicatorAnim = getIndicatorAnimator(false);
        setUpAnimationLisForExposeOpen(indicatorAnim, animatorValue);
        animatorValue.start();
    }

    //
    private void setUpAnimationLisForExposeOpen(final AnimatorValue indicatorAnim, AnimatorValue heightAnim) {
        heightAnim.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                compactCalendarController.setAnimationStatus(CompactCalendarController.EXPOSE_CALENDAR_ANIMATION);
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                indicatorAnim.start();
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });

        indicatorAnim.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                compactCalendarController.setAnimationStatus(CompactCalendarController.ANIMATE_INDICATORS);
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                compactCalendarController.setAnimationStatus(CompactCalendarController.IDLE);
                onOpen();
                isAnimating = false;
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
    }

    private AnimatorValue getIndicatorAnimator(boolean isFlag) {
        AnimatorValue animIndicator = new AnimatorValue();
        animIndicator.setDuration(INDICATOR_ANIM_DURATION_MILLIS);
        float dayIndicatorRadius = compactCalendarController.getDayIndicatorRadius();
        animIndicator.setValueUpdateListener((animatorValue, progress) -> {
            compactCalendarController.setGrowFactorIndicator(isFlag ?
                    (dayIndicatorRadius * progress < 1 ? 1f : dayIndicatorRadius * progress) :
                    ((1 - progress) * dayIndicatorRadius < 1 ? 1f : (1 - progress) * dayIndicatorRadius));
            compactCalendarView.invalidate();
        });
        return animIndicator;
    }

    private int getTargetGrowRadius() {
        int heightSq = compactCalendarController.getTargetHeight() *
                compactCalendarController.getTargetHeight();
        int widthSq = compactCalendarController.getWidth() *
                compactCalendarController.getWidth();
        return (int) (0.5 * Math.sqrt(heightSq + widthSq));
    }

    private void onOpen() {
        if (compactCalendarAnimationListener != null) {
            compactCalendarAnimationListener.onOpened();
        }
    }

    private void onClose() {
        if (compactCalendarAnimationListener != null) {
            compactCalendarAnimationListener.onClosed();
        }
    }

    private void setUpAnimationLisForOpen(AnimatorValue openAnimation) {
        openAnimation.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                onOpen();
                isAnimating = false;
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
    }

    private void setUpAnimationLisForClose(AnimatorValue openAnimation) {
        openAnimation.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
                onClose();
                isAnimating = false;
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
    }

    public boolean isAnimating() {
        return isAnimating;
    }
}
